#include "StdAfx.h"
#include <boost/scoped_array.hpp>
#include "../Engine/MapRenderer.hpp"
#include "../Engine/Map.hpp"
#include "../Engine/TerrainTile.hpp"
#include "../Engine/TextureAtlasInfo.hpp"
#include "../Engine/TerrainTileSet.hpp"
#include "../Engine/TerrainTileSplat.hpp"
#include "../Engine/QueryFlag.hpp"

#define HE_DEBUG_HILITE_TILE_INFO 0

namespace he
{
	std::string const MapRenderer::MAP_OBJECT_NAME = "main_map";
	std::string const MapRenderer::HIGHLIGHT_OBEJCT_NAME = "high_light";
	std::string const MapRenderer::HIGHLIGHT_MATERIAL_NAME = "he/highlite";
	std::string const MapRenderer::HIGHLIGHT_MATERIAL_NAME_MAIN = "he/highlitesel";
	std::string const MapRenderer::AXIS_MATERIAL_NAME = "he/axis";

	MapRenderer::MapRenderer(Map const *pMap, TerrainMaterialManager const &TerrainMaterialManager, Ogre::SceneManager &Manager)
		: m_pMap(pMap), m_TerrainMaterialManager(TerrainMaterialManager), m_SceneManager(Manager)
	{
		assert(m_pMap->get_width() % PARTITION_SIZE == 0 && m_pMap->get_height() % PARTITION_SIZE == 0 &&  "Bad map size");
		// Create axis object
		Ogre::ManualObject *p_object_axis = m_SceneManager.createManualObject("Axis");
		p_object_axis->setQueryFlags(0);
		p_object_axis->begin(AXIS_MATERIAL_NAME, Ogre::RenderOperation::OT_LINE_LIST);
		p_object_axis->position(0.0, 0.0, -50.0);
		p_object_axis->position(0.0, 0.0, 50.0);
		p_object_axis->position(-20.0, 0.0, 0.0);
		p_object_axis->position(20.0, 0.0, 0.0);
		p_object_axis->end();
		m_SceneManager.getRootSceneNode()->createChildSceneNode()->attachObject(p_object_axis);
		// Create scene node for highliting
		m_pHighlightSceneNode = 
			m_SceneManager.getRootSceneNode()->createChildSceneNode("hl_node"); 
		// Create highlight manual object
		m_pHighlightObject = m_SceneManager.createManualObject(HIGHLIGHT_OBEJCT_NAME); 
		m_pHighlightObject->setDynamic(true);
		m_pHighlightObject->setQueryFlags(0);
		// Draw main tile part
		m_pHighlightObject->begin(HIGHLIGHT_MATERIAL_NAME_MAIN,
			Ogre::RenderOperation::OT_TRIANGLE_LIST);
		for(unsigned short i = 0; i < 4; ++i)
			m_pHighlightObject->position(Ogre::Vector3(0, 0, 0));
		m_pHighlightObject->triangle(0, 1, 2);
		m_pHighlightObject->triangle(3, 2, 1);
		m_pHighlightObject->end();
		// Need to be above the terrain
        m_pHighlightObject->setRenderQueueGroup(m_pHighlightObject->getRenderQueueGroup() + 1);
		// Create other highlight objects
		m_pHighlightObject->begin(HIGHLIGHT_MATERIAL_NAME,
			Ogre::RenderOperation::OT_LINE_LIST);
		for(unsigned short i = 0, sub_res = 2; i < sub_res * sub_res; ++i)
			m_pHighlightObject->position(Ogre::Vector3(0, 0, 0));
		m_pHighlightObject->end();
		// Detach old object
		m_pHighlightSceneNode->attachObject(m_pHighlightObject);
	}

	void MapRenderer::create_map_model()
	{
		// Get number of partitions
		unsigned short partition_width = m_pMap->get_width() / PARTITION_SIZE;
		unsigned short partition_height = m_pMap->get_height() / PARTITION_SIZE;
		// Create vector of scene nodes
		m_PartitionSceneNodeVector.resize(PARTITION_SIZE);
		std::for_each(m_PartitionSceneNodeVector.begin(), m_PartitionSceneNodeVector.end(), std::bind(&SceneNodeVector_t::resize, std::placeholders::_1, 10));
		// Create partitions
		for(unsigned short i = 0; i < partition_height; ++i)
		{
			SceneNodeVector_t &scene_node_vector = m_PartitionSceneNodeVector.at(i);
			for(unsigned short j = 0; j < partition_width; ++j)
			{
				std::string name = MAP_OBJECT_NAME + Ogre::StringConverter::toString(j) + "_" + Ogre::StringConverter::toString(i);
				// Create manual object
				Ogre::ManualObject *p_object = m_SceneManager.createManualObject(name);
				// Set query mask
				p_object->setQueryFlags(QF_TERRAIN);
				// Save tha maxs
				unsigned short min_y = i * PARTITION_SIZE;
				unsigned short max_y = (i + 1) * PARTITION_SIZE;
				unsigned short min_x = j * PARTITION_SIZE;
				unsigned short max_x = (j + 1) * PARTITION_SIZE;
				// Create the min_max object
				TileMaxs_t min_maxs(min_x, min_y, max_x, max_y);
				// Add to map
				m_Partitions[name] = min_maxs;
				p_object->estimateVertexCount(PARTITION_SIZE * PARTITION_SIZE * 6);
				p_object->begin(get_map_material());
				p_object->setCastShadows(false);
				// Set the vertices
				update_partition(min_maxs, *p_object);
				p_object->end();
				// Attach to scene node
				scene_node_vector.at(j) = m_SceneManager.getRootSceneNode()->createChildSceneNode();
				scene_node_vector.at(j)->attachObject(p_object);

			}
		}
	}

	Ogre::Vector3 MapRenderer::compute_average_normal(TileContainer_t const &Tiles, unsigned char Vertex, TerrainTile const &CurrentTile)
	{
		Ogre::Vector3 normal(0, 0, 0);
		unsigned char number_of_normals = 0;
		switch(Vertex)
		{
		case 0:
			normal += tile_normal(CurrentTile, 0);
			++number_of_normals;
			if(Tiles[0] != nullptr)
			{
				normal += tile_normal(*Tiles[0], 1);
				++number_of_normals;
			}
			if(Tiles[1] != nullptr)
			{
				normal += tile_normal(*Tiles[1], 0) + tile_normal(*Tiles[1], 1);
				number_of_normals += 2;
			}
			if(Tiles[7] != nullptr)
			{
				normal += tile_normal(*Tiles[7], 0) + tile_normal(*Tiles[7], 1);
				number_of_normals += 2;
			}
			break;
		case 1:
			normal += tile_normal(CurrentTile, 0) + tile_normal(CurrentTile, 1);
			number_of_normals += 2;
			if(Tiles[5] != nullptr)
			{
				normal += tile_normal(*Tiles[5], 0);
				++number_of_normals;
			}
			if(Tiles[6] != nullptr)
			{
				normal += tile_normal(*Tiles[6], 0) + tile_normal(*Tiles[6], 1);
				number_of_normals += 2;
			}
			if(Tiles[7] != nullptr)
			{
				normal += tile_normal(*Tiles[7], 1);
				++number_of_normals;
			}
			break;
		case 2:
			normal += tile_normal(CurrentTile, 1);
			++number_of_normals;
			if(Tiles[3] != nullptr)
			{
				normal += tile_normal(*Tiles[3], 0) + tile_normal(*Tiles[3], 1);
				number_of_normals += 2;
			}
			if(Tiles[4] != nullptr)
			{
				normal += tile_normal(*Tiles[4], 0);
				++number_of_normals;
			}
			if(Tiles[5] != nullptr)
			{
				normal += tile_normal(*Tiles[5], 0) + tile_normal(*Tiles[5], 1);
				number_of_normals += 2;
			}
			break;
		case 3:
			normal += tile_normal(CurrentTile, 0) + tile_normal(CurrentTile, 1);
			number_of_normals += 2;
			if(Tiles[1] != nullptr)
			{
				normal += tile_normal(*Tiles[1], 1);
				++number_of_normals;
			}
			if(Tiles[2] != nullptr)
			{
				normal += tile_normal(*Tiles[2], 0) + tile_normal(*Tiles[2], 1);
				number_of_normals += 2;
			}
			if(Tiles[3] != nullptr)
			{
				normal += tile_normal(*Tiles[3], 0);
				++number_of_normals;
			}
			break;
		}
		// Make the average
		normal /= number_of_normals;
		// Normalize
		normal.normalise();

		return normal;
	}

	void MapRenderer::highlight_tile(TerrainTile const &Tile, std::uint_least8_t HighLightSize)
	{
#if HE_DEBUG_HILITE_TILE_INFO == 1
		// Some debug info
		std::cout << "Tile index (" << Tile.get_x() << ", " << Tile.get_y() << "): " << int(Tile.get_terrain_type_variant_index()) << ";Type: " <<
			int(Tile.get_terrain_type(0)) << ", " << int(Tile.get_terrain_type(1)) << ", " << int(Tile.get_terrain_type(2)) << ", " <<
			int(Tile.get_terrain_type(3)) << std::endl;
#endif // HE_DEBUG_HILITE_TILE_INFO == 1
		TileContainer_t neighbors;
		// Get all neighbors in "selection" size
		m_pMap->get_all_neighbors(Tile, HighLightSize, neighbors);
		// Update the main highlight tile
		{
			m_pHighlightObject->beginUpdate(0);
			TerrainTile const *p_neigh_tile = *neighbors.begin();
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 0));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 1));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 2));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 3));

			m_pHighlightObject->triangle(0, 1, 3);
			m_pHighlightObject->triangle(1, 2, 3);

			m_pHighlightObject->end();
		}
		// Update other highlight tiles
		m_pHighlightObject->beginUpdate(1);
		for(TileContainer_t::iterator it_neig = neighbors.begin() + 1; it_neig != neighbors.end(); ++it_neig)
		{
			TerrainTile const *p_neigh_tile = *it_neig;
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 0));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 1));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 1));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 2));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 2));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 3));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 3));
			m_pHighlightObject->position(tile_vertex(*p_neigh_tile, 0));
		}
		m_pHighlightObject->end();
	}

	TerrainTile const *MapRenderer::get_tile(Ogre::RaySceneQueryResult const &Rays, Ogre::Ray const &ViewRay) const
	{
		TerrainTile const *p_tile = nullptr;
		// Find the main map
		for(auto it = Rays.begin(); it != Rays.end(); ++it)
		{
			if(it->movable->getName().substr(0, MAP_OBJECT_NAME.size()) == MAP_OBJECT_NAME)
			{
				TileMaxs_t const &Maxs = m_Partitions.find(it->movable->getName())->second;
				for(unsigned short y = Maxs.get<1>(); y < Maxs.get<3>(); ++y)
				{
					for(unsigned short x = Maxs.get<0>(); x < Maxs.get<2>(); ++x)
					{
						TerrainTile *p_tile_now = &m_pMap->get_tile(x, y);
						// Find if tile intersect
						if(Ogre::Math::intersects(ViewRay, tile_vertex(*p_tile_now, 0), tile_vertex(*p_tile_now, 1), tile_vertex(*p_tile_now, 3)).first
							|| Ogre::Math::intersects(ViewRay, tile_vertex(*p_tile_now, 1), tile_vertex(*p_tile_now, 2), tile_vertex(*p_tile_now, 3)).first)
							p_tile = p_tile_now;
					}
				}
			}
		}
		return p_tile;
	}

	TerrainTile const * MapRenderer::get_tile_flat( Ogre::Vector3 const &Positon ) const
	{
		TerrainTile const *p_tile = nullptr;
		// Get rectangle tile in which accurate finding will occur
		int const tile_x = std::floor(Positon.x / (2 * TerrainTile::get_tile_width()));
		int const tile_y = std::floor(Positon.z / (2 * TerrainTile::get_tile_height())) * 2;
		// Tile must be valid
		if(tile_x >= 0 && tile_y >= 0)
		{
			// Reference tile
			TerrainTile const *p_ref_tile = &m_pMap->get_tile(tile_x, tile_y);
			// Get offset
			float offset_x = tile_x * TerrainTile::get_tile_width() * 2;
			float offset_y = tile_y * TerrainTile::get_tile_height();
			// Normalize position
			// We must normalize the whole position to range <-width, width> for x
			// and <-height,height> for z coordinate to be able use functions
			// below for getting the precise tile
			float normal_x = Positon.x - offset_x - TerrainTile::get_tile_width();
			float normal_y = TerrainTile::get_tile_height() - (Positon.z - offset_y);
			//       +h
			//    |-------|
			//    |1 / \ 0|
			//    | /   \ |
			// -w | \ 4 / | +w
			//    |2 \ / 3|
			//    |-------|
			//       -h
			// Function of "0"
			float f1 = - normal_x + TerrainTile::get_tile_width();
			// Function of "1"
			float f2 = normal_x + TerrainTile::get_tile_width();
			// Function of "2"
			float f3 = -normal_x - TerrainTile::get_tile_width();
			// Function of "3"
			float f4 = normal_x - TerrainTile::get_tile_width();

			// Default is actual tile
			int tile_pos = 4;
			if(normal_x > 0)
			{
				if(normal_y > 0)
				{
					if(normal_y > f1)
						tile_pos = 0;
				}
				else
				{
					if(normal_y < f4)
						tile_pos = 3;
				}
			}
			else
			{
				if(normal_y > 0)
				{
					if(normal_y > f2)
						tile_pos = 1;
				}
				else
				{
					if(normal_y < f3)
						tile_pos = 2;
				}
			}
			// Get tile on given position
			p_tile = p_ref_tile;
			switch(tile_pos)
			{
			case 0:
				p_tile = m_pMap->get_neighbor(*p_ref_tile, TileWalker::NORTH_EAST);
				break;
			case 1:
				p_tile = m_pMap->get_neighbor(*p_ref_tile, TileWalker::NORTH_WEST);
				break;
			case 2:
				p_tile = m_pMap->get_neighbor(*p_ref_tile, TileWalker::SOUTH_WEST);
				break;
			case  3:
				p_tile = m_pMap->get_neighbor(*p_ref_tile, TileWalker::SOUTH_EAST);
				break;
				// Do nothing, already got the tile
			case 4:
				break;
			}
		}
		return p_tile;
	}

	TerrainTile const * MapRenderer::get_tile( Ogre::Vector3 const &Positon ) const
	{
		// Create ray down the Y-axis
		// Need to move origin above 1 unit to be able query terrain
		Ogre::Ray ray(Positon + Ogre::Vector3::UNIT_Y, Ogre::Vector3::NEGATIVE_UNIT_Y);
		// Create scene query
		Ogre::RaySceneQuery *p_ray_query = m_SceneManager.createRayQuery(ray, QF_TERRAIN);
		// Don't select unneeded stuff
		p_ray_query->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
		Ogre::RaySceneQueryResult &rs = p_ray_query->execute();

		return get_tile(rs, ray);
	}

	void MapRenderer::update_tile(TileContainer_t const &Tiles)
	{
		// Get the partitions that need to be updated
		typedef std::set<std::string> PartitionSet_t;
		PartitionSet_t partitions;
		for(auto it = Tiles.begin(); it != Tiles.end(); ++it)
		{
			// Only valid tiles
			if(*it != nullptr)
			{
				// Get the partition that the tile is inside
				unsigned short x = (*it)->get_x() / PARTITION_SIZE;
				unsigned short y = (*it)->get_y() / PARTITION_SIZE;
				partitions.insert(MAP_OBJECT_NAME + Ogre::StringConverter::toString(x) + "_" + Ogre::StringConverter::toString(y));
			}
		}
		// Update partitions
		for(PartitionSet_t::iterator it = partitions.begin(); it != partitions.end(); ++it)
		{
			// Get the manual object to update
			Ogre::ManualObject *p_object = m_SceneManager.getManualObject(*it);
			// Get the partition
			TileMaxs_t const &min_maxs = m_Partitions.find(*it)->second;
			// Begin the update
			p_object->beginUpdate(0);
			update_partition(min_maxs, *p_object);
			p_object->end();
		}
	}

	void MapRenderer::update_partition(TileMaxs_t const &MaxMin, Ogre::ManualObject &Object)
	{
		// Tile vertices:
		//     0
		//   /   \
		//  /  1  \
		// 1 ----- 3
		//  \  2  /
		//   \   /
		//     2
		int index = 4;
		// Create container for neighbors and reserve size
		TileContainer_t neighbors;
		neighbors.reserve(TileWalker::NORTH_WEST - TileWalker::NORTH + 1);
		// Get terrain tile set
		TerrainTileSet const &terrain_set = m_TerrainMaterialManager.get_terrain_set(m_pMap->get_map_type());
		// Traverse all tiles
		for(unsigned short y = MaxMin.get<1>(), y_max = MaxMin.get<3>(); y < y_max; ++y)
		{
			for(unsigned short x = MaxMin.get<0>(), x_max = MaxMin.get<2>(); x < x_max; ++x, index += 4)
			{
				// Get the tile
				TerrainTile const &tile = m_pMap->get_tile(x, y);
				float p_v0y = tile.vertex_deep(0), p_v1y = tile.vertex_deep(1), p_v2y = tile.vertex_deep(2), p_v3y = tile.vertex_deep(3);
				// Set the tile width used with texture coordinates
				float tex_coord_width = 2 * (1.0f / (x_max - MaxMin.get<0>())), tex_coord_height = 2 * (1.0f / (MaxMin.get<3>() - MaxMin.get<1>()));
				// Compute tile texture coordinates in <0, 1> range
				float x_tex_pos = ((y % 2) * tex_coord_width / 2) + tex_coord_width / 2 + x * tex_coord_width;
				float y_tex_pos = y * tex_coord_height / 2 + tex_coord_height / 2;
				// Get all tile neighbors
				neighbors.clear();
				m_pMap->get_all_neighbors(tile, neighbors);
				// Calculate average normal vector for each vertex
				Ogre::Vector3 const n0 = compute_average_normal(neighbors, 0, tile),
					n1 = compute_average_normal(neighbors, 1, tile),
					n2 = compute_average_normal(neighbors, 2, tile),
					n3 = compute_average_normal(neighbors, 3, tile);
				// Get the tile material types
				unsigned short mat_v0 = tile.get_terrain_type(0), mat_v1 = tile.get_terrain_type(1), mat_v2 = tile.get_terrain_type(2), mat_v3 = tile.get_terrain_type(3);
				// Get 1. and 2. material
				unsigned short mat_1 = mat_v0;
				unsigned short mat_2 = mat_1;
				if(mat_v0 != mat_v1)
					mat_2 = mat_v1;
				else if(mat_v0 != mat_v2)
					mat_2 = mat_v2;
				else if(mat_v0 != mat_v3)
					mat_2 = mat_v3;
				// Get alpha splat index
				unsigned short alpha_index = 1;
				alpha_index |= (mat_v1 == mat_1) ? 2 : 0;
				alpha_index |= (mat_v2 == mat_1) ? 4 : 0;
				alpha_index |= (mat_v3 == mat_1) ? 8 : 0;
				// If materials need to be inverted
				if(alpha_index > 7)
				{
					std::swap(mat_1, mat_2);
					alpha_index = ~alpha_index & 15;
				}
				// Get the primary tile type information
				TextureAtlasInfo_t const &p_primary_mat_info = terrain_set.get_tile_type(mat_1, tile.get_terrain_type_variant_index());
//d				TileTypeInfo_t const * const p_primary_mat_info = &terrain_mat.get_tile_info(mat_1, 0);
				// Get secondary tile type information
				TextureAtlasInfo_t const &p_secondary_mat_info = terrain_set.get_tile_type(mat_2, tile.get_terrain_type_variant_index());
//d				TileTypeInfo_t const * const p_secondary_mat_info = &terrain_mat.get_tile_info(mat_2, 0);
				// Get the alpha splat info for tile
				TextureAtlasInfo_t const &p_alpha_splat_mat_info = terrain_set.get_splat().get_info(alpha_index);
				// Texture coordinates
				float tex_coord_x, tex_coord_y;

				// North
				Object.position(tile_vertex(tile, 0));
				Object.normal(n0);
				// Primary terrain type texture
				Object.textureCoord(std::get<0>(p_primary_mat_info) + std::get<2>(p_primary_mat_info) / 2, std::get<1>(p_primary_mat_info));
				// Alpha splat
				Object.textureCoord(std::get<0>(p_alpha_splat_mat_info) + std::get<2>(p_alpha_splat_mat_info) / 2, std::get<1>(p_alpha_splat_mat_info));
				// Secondary terrain type texture
				Object.textureCoord(std::get<0>(p_secondary_mat_info) + std::get<2>(p_secondary_mat_info) / 2, std::get<1>(p_secondary_mat_info));
				// Noise
				tex_coord_x = x_tex_pos;
				tex_coord_y = y_tex_pos - tex_coord_height / 2;
				Object.textureCoord(tex_coord_x, tex_coord_y);

				// West
				Object.position(tile_vertex(tile, 1));
				Object.normal(n1);
				// Primary terrain type texture
				Object.textureCoord(std::get<0>(p_primary_mat_info), std::get<1>(p_primary_mat_info) + std::get<2>(p_primary_mat_info) / 2);
				// Alpha splat
				Object.textureCoord(std::get<0>(p_alpha_splat_mat_info), std::get<1>(p_alpha_splat_mat_info) + std::get<3>(p_alpha_splat_mat_info) / 2);
				// Secondary terrain type texture
				Object.textureCoord(std::get<0>(p_secondary_mat_info), std::get<1>(p_secondary_mat_info) + std::get<3>(p_secondary_mat_info) / 2);
				// Noise
				tex_coord_x = x_tex_pos - tex_coord_width / 2;
				tex_coord_y = y_tex_pos;
				Object.textureCoord(tex_coord_x, tex_coord_y);

				// South
				Object.position(tile_vertex(tile, 2));
				Object.normal(n2);
				// Primary terrain type texture
				Object.textureCoord(std::get<0>(p_primary_mat_info) + std::get<2>(p_primary_mat_info) / 2, std::get<1>(p_primary_mat_info) + std::get<3>(p_primary_mat_info));
				// Alpha splat
				Object.textureCoord(std::get<0>(p_alpha_splat_mat_info) + std::get<2>(p_alpha_splat_mat_info) / 2, std::get<1>(p_alpha_splat_mat_info) + std::get<3>(p_alpha_splat_mat_info));
				// Secondary terrain type texture
				Object.textureCoord(std::get<0>(p_secondary_mat_info) + std::get<2>(p_secondary_mat_info) / 2, std::get<1>(p_secondary_mat_info) + std::get<3>(p_secondary_mat_info));
				// Noise
				tex_coord_x = x_tex_pos;
				tex_coord_y = y_tex_pos + tex_coord_height / 2;
				Object.textureCoord(tex_coord_x, tex_coord_y);

				// East
				Object.position(tile_vertex(tile, 3));
				Object.normal(n3);
				// Primary terrain type texture
				Object.textureCoord(std::get<0>(p_primary_mat_info) + std::get<2>(p_primary_mat_info), std::get<1>(p_primary_mat_info) + std::get<3>(p_primary_mat_info) / 2);
				// Alpha splat
				Object.textureCoord(std::get<0>(p_alpha_splat_mat_info) + std::get<2>(p_alpha_splat_mat_info), std::get<1>(p_alpha_splat_mat_info) + std::get<3>(p_alpha_splat_mat_info) / 2);
				// Secondary terrain type texture
				Object.textureCoord(std::get<0>(p_secondary_mat_info) + std::get<2>(p_secondary_mat_info), std::get<1>(p_secondary_mat_info) + std::get<3>(p_secondary_mat_info) / 2);
				// Noise
				tex_coord_x = x_tex_pos + tex_coord_width / 2;
				tex_coord_y = y_tex_pos;
				Object.textureCoord(tex_coord_x, tex_coord_y);

				// Draw the triangles
				Object.triangle(index - 4, index - 3, index - 1);
				Object.triangle(index - 3, index - 2, index - 1);
			}
		}
	}

	void MapRenderer::enable_noise(bool Enable)
	{
		// Get the material
		Ogre::MaterialPtr p_mat = Ogre::MaterialManager::getSingleton().load(get_map_material(), "General");
		// Get the noise pass
		Ogre::Pass *p_pass = p_mat->getTechnique(0)->getPass(0);
		// Don't use noise texture
		if(Enable)
			p_pass->setFragmentProgram("splat_noise");
		else
			p_pass->setFragmentProgram("splat");

	}

	std::string const MapRenderer::get_map_material() const
	{
		return m_TerrainMaterialManager.get_terrain_set(m_pMap->get_map_type()).get_material_name();
	}

	MapRenderer::RenderOpVector_t const MapRenderer::get_partitions_data() const
	{
		RenderOpVector_t render_ops;
		for(ObjectMap_t::const_iterator it = m_Partitions.begin(), end = m_Partitions.end(); it != end; ++it)
		{
			// Get manual object
			Ogre::ManualObject *p_object = m_SceneManager.getManualObject(it->first);
			// Should have only 1 subsection
			assert(p_object->getNumSections() == 1);
			render_ops.push_back(p_object->getSection(0)->getRenderOperation());
		}

		return render_ops;
	}

	Ogre::Vector3 MapRenderer::tile_vertex(TerrainTile const &Tile, std::uint_least8_t Vertex)
	{
		Ogre::Vector3 out;

		float x_pos = float(((Tile.get_y() % 2) * TerrainTile::get_tile_width()) + TerrainTile::get_tile_width() + Tile.get_x() * TerrainTile::get_tile_width() * 2);
		float y_pos = float(Tile.get_y() * TerrainTile::get_tile_width() + TerrainTile::get_tile_width());

		switch(Vertex)
		{
		case 0:
			out = Ogre::Vector3(x_pos, Tile.vertex_deep(0), y_pos - float(TerrainTile::get_tile_height()));
			break;
		case 1:
			out = Ogre::Vector3(x_pos - float(TerrainTile::get_tile_width()), Tile.vertex_deep(1), y_pos);
			break;
		case 2:
			out = Ogre::Vector3(x_pos, Tile.vertex_deep(2), y_pos + float(TerrainTile::get_tile_height()));
			break;
		case 3:
			out = Ogre::Vector3(x_pos + float(TerrainTile::get_tile_width()), Tile.vertex_deep(3), y_pos);
			break;
		default:
			throw std::logic_error("MapRenderer: Unknown tile vertex number.");
			break;
		}

		return out;
	}

	Ogre::Vector3 MapRenderer::tile_normal(TerrainTile const &Tile, std::uint_least8_t Face)
	{
		Ogre::Vector3 out;

		// Get all vertices
		Ogre::Vector3 const n0 = tile_vertex(Tile, 0);
		Ogre::Vector3 const n1 = tile_vertex(Tile, 1); 
		Ogre::Vector3 const n2 = tile_vertex(Tile, 2);
		Ogre::Vector3 const n3 = tile_vertex(Tile, 3);

		switch(Face)
		{
		case 0:
			out = Ogre::Math::calculateBasicFaceNormal(n0, n1, n3);
			break;
		case 1:
			out= Ogre::Math::calculateBasicFaceNormal(n1, n2, n3);
			break;
		default:
			throw std::logic_error("MapRenderer: Unknown tile triangle face.");
			break;
		}

		return out;
	}
}